/*******************************************************************************
 * 修改版MQTT共享内存监控程序
 * 功能：
 * 1. 循环查找共享内存中key值为1-14的所有节点
 * 2. 将找到的节点的new_val通过MQTT发布
 * 3. 支持优雅退出和资源清理
 *******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include "MQTTClient.h"
#include "shmem.h"
#include <stdbool.h>  // 添加这行以支持bool类型
// ==================== 配置参数 ====================
// MQTT连接参数
#define MQTT_ADDRESS     "tcp://192.168.147.174:1883"  // MQTT代理地址
#define CLIENT_ID        "SharedMemMonitor"         // 客户端ID
#define PUB_TOPIC        "up"                       // 发布主题
#define SUB_TOPIC        "down"                     // 订阅主题
#define QOS_LEVEL        1                          // 服务质量等级
#define MQTT_TIMEOUT     10000L                     // 超时时间(毫秒)

// 共享内存配置
#define SHM_NAME        "shm_test"                  // 共享内存名称
#define SHM_SIZE        4096                        // 共享内存大小

// 监控参数
#define MIN_KEY         1                           // 最小key值
#define MAX_KEY         14                          // 最大key值
#define MONITOR_INTERVAL 1000000                    // 监控间隔(微秒)

// ==================== 数据结构 ====================
// 共享内存数据类型
typedef union {
    int b_val;   // 布尔型
    int i_val;   // 整型
    float f_val; // 浮点型
} val_t;

// 共享内存节点结构
typedef struct {
    int key;       // 节点唯一标识
    int type;      // 数据类型 (0:bool, 1:int, 2:float)
    int dev_type;  // 设备类型
    val_t old_val; // 旧值
    val_t new_val; // 新值
    int ret;       // 状态码
} std_node;

// ==================== 全局变量 ====================
volatile sig_atomic_t running = 1;      // 程序运行标志
volatile MQTTClient_deliveryToken delivered_token; // 消息令牌

// ==================== 函数声明 ====================
void handle_signal(int sig);
void publish_mqtt_value(MQTTClient client, std_node *node);
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message);
void connlost(void *context, char *cause);
void delivered(void *context, MQTTClient_deliveryToken dt);
static float key_values[14] = {0};
static int keys_initialized = 0;
// ==================== 主函数 ====================
int main(int argc, char* argv[]) {
    // ---------- 1. 初始化信号处理 ----------
    signal(SIGINT, handle_signal);
    signal(SIGTERM, handle_signal);
    printf("启动共享内存监控服务...\n");

    // ---------- 2. 初始化共享内存 ----------
    struct shm_param shm_para;
    int ret = shm_init(&shm_para, SHM_NAME, SHM_SIZE);
    if (ret < 0) {
        fprintf(stderr, "共享内存初始化失败\n");
        return EXIT_FAILURE;
    }

    // 获取共享内存地址
    int *p_num = (int *)shm_getaddr(&shm_para);
    if (p_num == NULL) {
        fprintf(stderr, "共享内存映射失败\n");
        shm_del(&shm_para);
        return EXIT_FAILURE;
    }

    int num_nodes = *p_num; // 读取节点数量
    std_node *nodes = (std_node *)(p_num + 1); // 跳过节点数量字段
    
    printf("共享内存中共有 %d 个节点...\n", num_nodes);
    printf("正在监控key为 %d-%d 的节点...\n", MIN_KEY, MAX_KEY);

    // ---------- 3. 初始化MQTT客户端 ----------
    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    
    // 创建客户端
    MQTTClient_create(&client, MQTT_ADDRESS, CLIENT_ID, 
                     MQTTCLIENT_PERSISTENCE_NONE, NULL);
    
    // 设置连接选项
    conn_opts.keepAliveInterval = 20;
    conn_opts.cleansession = 1;
    
    // 设置回调函数
    MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered);
    
    // 连接到MQTT代理
    if ((ret = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
        fprintf(stderr, "MQTT连接失败: %d\n", ret);
        shm_del(&shm_para);
        return EXIT_FAILURE;
    }
    
    printf("已连接到MQTT代理: %s\n", MQTT_ADDRESS);
    
    // 订阅主题(可选)
    printf("订阅主题: %s\n", SUB_TOPIC);
    MQTTClient_subscribe(client, SUB_TOPIC, QOS_LEVEL);

    // ---------- 4. 主监控循环 ----------
    printf("监控运行中... 按Q退出\n");
    while (running) {
        // 查找所有目标key的节点
        for (int target_key = MIN_KEY; target_key <= MAX_KEY; target_key++) {
            std_node *target_node = NULL;
            for (int i = 0; i < num_nodes; i++) {
                if (nodes[i].key == target_key) {
                    target_node = &nodes[i];
                    break;
                }
            }
            
            if (target_node != NULL) {
                // 打印当前值
                printf("找到key %d 的节点: ", target_key);
                switch (target_node->type) {
                    case 0: // 布尔型
                        printf("值=%d (布尔)\n", target_node->new_val.b_val);
                        break;
                    case 1: // 整型
                        printf("值=%d (整数)\n", target_node->new_val.i_val);
                        break;
                    case 2: // 浮点型
                        printf("值=%.2f (浮点)\n", target_node->new_val.f_val);
                        break;
                }
                
                // 发布MQTT消息
                publish_mqtt_value(client, target_node);
            } else {
                fprintf(stderr, "未找到key为 %d 的节点\n", target_key);
            }
        }
        
        // 检查用户输入(非阻塞)
        fd_set fds;
        struct timeval tv;
        FD_ZERO(&fds);
        FD_SET(STDIN_FILENO, &fds);
        tv.tv_sec = 0;
        tv.tv_usec = 100000; // 100ms
        
        if (select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv) > 0) {
            if (FD_ISSET(STDIN_FILENO, &fds)) {
                char ch = getchar();
                if (ch == 'Q' || ch == 'q') {
                    running = 0;
                }
            }
        }
        
        usleep(MONITOR_INTERVAL); // 按指定间隔监控
    }

    // ---------- 5. 资源清理 ----------
    printf("\n正在关闭服务...\n");
    MQTTClient_unsubscribe(client, SUB_TOPIC);
    MQTTClient_disconnect(client, MQTT_TIMEOUT);
    MQTTClient_destroy(&client);
    shm_del(&shm_para);
    
    printf("服务已安全停止\n");
    return EXIT_SUCCESS;
}

// ==================== 功能函数实现 ====================

// 信号处理函数
void handle_signal(int sig) {
    printf("接收到信号 %d, 准备退出...\n", sig);
    running = 0;
}


void publish_mqtt_value(MQTTClient client, std_node *node) {
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;
    char payload[512];  // 扩大缓冲区以适应更复杂的JSON结构
    char val_str[32];    // 用于存储转换后的字符串值
    
    // 根据数据类型转换值为字符串
    switch (node->type) {
        case 0:  // 布尔型
            snprintf(val_str, sizeof(val_str), "%d", node->new_val.b_val ? 1 : 0);
            break;
        case 1:  // 整型
            snprintf(val_str, sizeof(val_str), "%d", node->new_val.i_val);
            break;
        case 2:  // 浮点型
            snprintf(val_str, sizeof(val_str), "%.2f", node->new_val.f_val);
            break;
        default: // 未知类型
            snprintf(val_str, sizeof(val_str), "0");
    }
    
    // 构建JSON格式的消息
    snprintf(payload, sizeof(payload),
        "{\"type\":1,\"result\":0,\"data\":["
        "{\"key\":%d,\"val\":\"%s\"}"
        "]}",
        node->key,
        val_str
    );
    
    pubmsg.payload = payload;
    pubmsg.payloadlen = (int)strlen(payload);
    pubmsg.qos = QOS_LEVEL;
    pubmsg.retained = 0;
    
    int rc = MQTTClient_publishMessage(client, PUB_TOPIC, &pubmsg, &token);
    if (rc != MQTTCLIENT_SUCCESS) {
        fprintf(stderr, "消息发布失败, 错误码 %d\n", rc);
        return;
    }
    
    rc = MQTTClient_waitForCompletion(client, token, MQTT_TIMEOUT);
    if (rc != MQTTCLIENT_SUCCESS) {
        fprintf(stderr, "消息投递失败, 错误码 %d\n", rc);
    } else {
        printf("已发布当前值: %s\n", payload);
    }
}




// MQTT消息到达回调
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message) {
    printf("\n收到MQTT消息\n");
    printf("  主题: %s\n", topicName);
    printf("  内容: %.*s\n", message->payloadlen, (char*)message->payload);
    
    MQTTClient_freeMessage(&message);
    MQTTClient_free(topicName);
    return 1;
}

// 连接丢失回调
void connlost(void *context, char *cause) {
    printf("\nMQTT连接丢失\n");
    printf("  原因: %s\n", cause);
    running = 0;  // 触发程序退出
}

// 消息投递确认回调
void delivered(void *context, MQTTClient_deliveryToken dt) {
    printf("消息(令牌%d)投递确认\n", dt);
    delivered_token = dt;
}